package com.wfh.picture.manager.websocket;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.wfh.picture.manager.websocket.disruptor.PictureEditEventProducer;
import com.wfh.picture.manager.websocket.model.PictureEditMessageTypeEnum;
import com.wfh.picture.manager.websocket.model.PictureEditRequestMessage;
import com.wfh.picture.manager.websocket.model.PictureEditResponseMessage;
import com.wfh.picture.model.entity.User;
import com.wfh.picture.service.UserService;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author FengHuan Wang
 * @Date 2025/5/18 9:31
 * @Version 1.0
 */
@Component
public class PictureEditHandler extends TextWebSocketHandler {


    @Resource
    private UserService userService;

    @Resource
    private PictureEditEventProducer pictureEditEventProducer;


    // 每张图片的编辑状态，key: pictureId, value: 当前正在编辑的用户 ID
    private final Map<Long, Long> pictureEditingUsers = new ConcurrentHashMap<>();

    // 保存所有连接的会话，key: pictureId, value: 用户会话集合
    private final Map<Long, Set<WebSocketSession>> pictureSessions = new ConcurrentHashMap<>();


    /**
     * 连接建立成功
     *
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 保存会话到集合中
        User user = (User) session.getAttributes().get("user");
        Long pictureId = (Long) session.getAttributes().get("pictureId");
        pictureSessions.computeIfAbsent(pictureId, k -> ConcurrentHashMap.newKeySet()).add(session);
        pictureSessions.get(pictureId).add(session);
        // 构造相应，发送加入编辑的消息通知
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        pictureEditResponseMessage.setMessage("用户 " + user.getUserName() + " 加入编辑");
        pictureEditResponseMessage.setUser(userService.getUserVO(user));
        // 广播给所有的用户
        broadcastPictureEditMessage(pictureId, pictureEditResponseMessage, session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 获取消息列别
        User user = (User) session.getAttributes().get("user");
        Long pictureId = (Long) session.getAttributes().get("pictureId");
        PictureEditRequestMessage pictureEditRequestMessage = JSONUtil.toBean(message.getPayload(), PictureEditRequestMessage.class);
       // 发送到队列
        pictureEditEventProducer.publishEvent(pictureEditRequestMessage, session, user, pictureId);
    }

    /**
     * 处理编辑
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     */
    public void editAction(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) {
        Long editingUserId = pictureEditingUsers.get(pictureId);
        String editAction = pictureEditRequestMessage.getEditAction();
        if (editAction == null) {
            return;
        }
        // 确认是当前的编辑者
        if (editingUserId.equals(user.getId())) {
            // 执行编辑动作
            // 构造相应，发送编辑动作的消息通知
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
            pictureEditResponseMessage.setMessage("用户 " + user.getUserName() + " 执行编辑动作");
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            pictureEditResponseMessage.setEditAction(pictureEditRequestMessage.getEditAction());
            // 广播给所有的用户
            broadcastPictureEditMessage(pictureId, pictureEditResponseMessage, session);
        }
    }

    /**
     * 推出编辑
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     */
    public void exitEdit(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) {
        // 退出编辑
        pictureEditingUsers.remove(pictureId);
        // 构造相应，发送退出编辑的消息通知
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        pictureEditResponseMessage.setMessage("用户 " + user.getUserName() + " 退出编辑");
        pictureEditResponseMessage.setUser(userService.getUserVO(user));
        // 广播给所有的用户
        broadcastPictureEditMessage(pictureId, pictureEditResponseMessage, session);
    }

    /**
     *
     * 进入编辑
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     * @throws IOException
     */
    public void enterEdit(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws IOException {
        // 判断是否已经有人在编辑
        if (pictureEditingUsers.containsKey(pictureId)) {
            // 构造相应，发送错误消息通知
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ERROR.getValue());
            pictureEditResponseMessage.setMessage("当前图片已经有用户在编辑");
            // 发送消息
            session.sendMessage(new TextMessage(JSONUtil.toJsonStr(pictureEditResponseMessage)));
        } else{
            // 进入编辑
            pictureEditingUsers.put(pictureId, user.getId());
            // 构造相应，
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
            pictureEditResponseMessage.setMessage("用户 " + user.getUserName() + " 进入编辑");
            pictureEditResponseMessage.setUser(userService.getUserVO(user));
            // 广播给所有的用户
            broadcastPictureEditMessage(pictureId, pictureEditResponseMessage, session);
        }

    }

    /**
     * 连接关闭
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        Long pictureId = (Long) session.getAttributes().get("pictureId");
        // 移除当前用户的编辑状态
        exitEdit(null, session, user, pictureId);
        // 删除会话
        Set<WebSocketSession> webSocketSessions = pictureSessions.get(pictureId);
        if (CollUtil.isNotEmpty(webSocketSessions)) {
            webSocketSessions.remove(session);
        }
    }

    /**
     * 广播给该图片的所有用户
     *
     * @param pictureId
     * @param message
     */
    private void broadcastPictureEditMessage(Long pictureId, PictureEditResponseMessage message, WebSocketSession excludeSession) {
        Set<WebSocketSession> sessions = pictureSessions.get(pictureId);
        if (CollUtil.isNotEmpty(sessions)) {
            // 解决精度丢失问题
            ObjectMapper objectMapper = new ObjectMapper();
            SimpleModule module = new SimpleModule();
            module.addSerializer(Long.class, ToStringSerializer.instance);
            module.addSerializer(Long.TYPE, ToStringSerializer.instance);
            objectMapper.registerModule(module);
            TextMessage textMessage = new TextMessage(JSONUtil.toJsonStr(message));
            sessions.forEach(session ->
            {
                // 排除掉的消息不发送
                if (session != excludeSession && session.isOpen()) {
                    try {
                        session.sendMessage(textMessage);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }
}
